﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SNFEGame.Visual;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;
using XmlDependent;

namespace SNFEGame.Util
{
    
    /// <summary>
    /// This SINGLETON stores the variables, images, etc. that the game will need in various places
    /// </summary>
    public class Storage
    {
        private static Dictionary<string, Vector2> blockTypes = new Dictionary<string, Vector2>();

        private static List<Texture2D> backgroundList = new List<Texture2D>();
        private static Dictionary<string, Texture2D> blockMap = new Dictionary<string, Texture2D>();
        private static Dictionary<string, Texture2D> fakeWindowMap = new Dictionary<string, Texture2D>();
        private static Dictionary<string, SpriteFont> fontMap = new Dictionary<string, SpriteFont>();

        private static string[] messages;
        public static Dictionary<string, Texture2D[]> characterAnimationMap;

        public static Texture2D boundingBox, screen, whiteDot;
        public static Texture2D sky, rocks;
        public static Texture2D titleArt;

        public static float finishLine;
        public static string[] test;

        public static SoundEffect run, jump;
        public static Song music;

        public static Block endFlag1, endFlag2;

        static Random generator = new Random();

        /// <summary>
        /// Loads all textures into the texture map for runtime access.
        /// </summary>
        /// <param name="content"></param>
        public static void Initialize(ContentManager content)
        {
            Random generator = new Random();
            messages = content.Load<string[]>("Xml/messages");
            test = content.Load<string[]>("Xml/tutorial");

            boundingBox = content.Load<Texture2D>("Images/Environment/Bounding Box");
            screen = content.Load<Texture2D>("Images/Environment/Screen");

            sky = content.Load<Texture2D>("Images/Environment/Background/skyYellow");
            rocks = content.Load<Texture2D>("Images/Environment/Background/wireFrame");

            titleArt = content.Load<Texture2D>("Images/Environment/Background/TitleArt");

            // Load fake window prompts
            foreach (FileInfo f in (new DirectoryInfo(content.RootDirectory + "/Images/Environment/FakeWindows").GetFiles("*.*")))
                fakeWindowMap[Path.GetFileNameWithoutExtension(f.Name)] = content.Load<Texture2D>("Images/Environment/FakeWindows/" + Path.GetFileNameWithoutExtension(f.Name));

            // Load background images
            foreach (FileInfo f in (new DirectoryInfo(content.RootDirectory + "/Images/Environment/Background").GetFiles("*.*")))
                backgroundList.Add(content.Load<Texture2D>("Images/Environment/Background/" + Path.GetFileNameWithoutExtension(f.Name)));

            // Load positions from xml
            foreach (BlockType b in content.Load<BlockType[]>("Xml/blocks"))
                blockTypes[b.type] = new Vector2(b.boundingWidth, b.boundingHeight);

            // Load fonts
            foreach (FileInfo f in (new DirectoryInfo(content.RootDirectory + "/Fonts").GetFiles("*.*")))
                fontMap[Path.GetFileNameWithoutExtension(f.Name)] = content.Load<SpriteFont>("Fonts/" + Path.GetFileNameWithoutExtension(f.Name));

            // Load block images
            foreach (FileInfo f in (new DirectoryInfo(content.RootDirectory + "/Images/Environment/Blocks")).GetFiles("*.*")) 
            {
                string key = Path.GetFileNameWithoutExtension(f.Name);
                if (key.Contains("["))
                    key = key.Substring(0, key.IndexOf("[") - 1);
                blockMap[key] = content.Load<Texture2D>("Images/Environment/Blocks/" + Path.GetFileNameWithoutExtension(f.Name));
            }

            // Load character sprite
            string characterBase = "/Images/Player/Gemini/";
            characterAnimationMap = new Dictionary<string, Texture2D[]>();
            characterAnimationMap[Character.JUMPING] = LoadAnimation(content, characterBase + Character.JUMPING);
            characterAnimationMap[Character.STANDING] = LoadAnimation(content, characterBase + Character.STANDING);
            characterAnimationMap[Character.WALKING] = LoadAnimation(content, characterBase + Character.WALKING);

            // Load Menu Cursor
            whiteDot = content.Load<Texture2D>("Images/Environment/Unused/10x10Dot");

            //Load sounds

            run = content.Load<SoundEffect>("Audio/run");
            music = content.Load<Song>("Audio/music");
            jump = content.Load<SoundEffect>("Audio/whip");
        }

        private static Texture2D[] LoadAnimation(ContentManager content, string dir)
        {
            FileInfo[] files = new DirectoryInfo(content.RootDirectory + dir).GetFiles("*.*");
            Texture2D[] textures = new Texture2D[files.Length];

            for (int i = 0; i < files.Length; i++)
                textures[i] = content.Load<Texture2D>(dir.Substring(1) + "/" + Path.GetFileNameWithoutExtension(files[i].Name));

            return textures;
        }

        /// <summary>
        /// Gets a texure given a name.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static Texture2D GetBackground(int index)
        {
            return backgroundList[index];
        }

        public static Texture2D GetBlockTexture(string name)
        {
            return blockMap[name];
        }

        public static Texture2D GetFakeWindow(string name)
        {
            return fakeWindowMap[name];
        }

        public static SpriteFont GetFont(string name)
        {
            return fontMap.ContainsKey(name) ? fontMap[name] : fontMap["Normal"];
        }

        public static string GetMessage(int level)
        {
            if (level - 1 < messages.Length)
                return messages[level - 1];
            else
                return messages[messages.Length - 1];
        }

        public static string getTutorial()
        {
           

            return test[0];
        }

        /// <summary>
        /// This sets up a new level for the game, based off of the xml file chosen
        /// </summary>
        /// <param name="content">it needed a contentmanager, not that I know why</param>
        /// <param name="filename">the name of the xml file that has the level data</param>
        /// <returns>A list of the environment objects to be placed in the world</returns>
        public static List<Sprite> setUpLevel(ContentManager content, string filename)
        {
            List<Sprite> blockList = new List<Sprite>();
            foreach (BlockData data in content.Load<BlockData[]>("Xml/" + filename))
            {
                blockList.Add(new Block(data, 
                    blockTypes.ContainsKey(data.type) ? new Vector2(blockTypes[data.type].X, blockTypes[data.type].Y) : Vector2.Zero));
            }
            return blockList;
        }

        private static  float MAX_DISTANCE_BETWEEN_BLOCKS = 400;
        private static  float MIN_DISTANCE_BETWEEN_BLOCKS = 200;
        private static  float MAX_VERTICAL_DISTANCE = 100;
        private static  int MIN_AMOUNT_BLOCKS = 40;
        private static  int MAX_AMOUNT_BLOCKS = 60;
        /// <summary>
        /// 1 all types, 2 medium and large, 3 only large
        /// </summary>
        private static int SMALLEST_AVAILABLE_BLOCK_SIZE = 1;
        /// <summary>
        /// 1 = all blocks, 2 = only disappearing and normal, 3+ only normal
        /// </summary>
        private static int BLOCK_VARIETY = 1;
        

        public static List<Sprite>  GenerateLevel(float startPoint, Character character, int level)
        {
            switch (level)
            {
                case(1):
                    MIN_AMOUNT_BLOCKS = 10;
                    MAX_AMOUNT_BLOCKS = 20;
                    SMALLEST_AVAILABLE_BLOCK_SIZE = 3;
                    BLOCK_VARIETY = 3;
                    break;
                case (2):
                    MIN_AMOUNT_BLOCKS = 10;
                    MAX_AMOUNT_BLOCKS = 20;
                    SMALLEST_AVAILABLE_BLOCK_SIZE = 2;
                    BLOCK_VARIETY = 2;
                    break;
                case (3):
                    MIN_AMOUNT_BLOCKS = 10;
                    MAX_AMOUNT_BLOCKS = 20;
                    SMALLEST_AVAILABLE_BLOCK_SIZE = 2;
                    BLOCK_VARIETY = 1;
                    break;
                default:
                    MIN_AMOUNT_BLOCKS = 40;
                    MAX_AMOUNT_BLOCKS = 60;
                    SMALLEST_AVAILABLE_BLOCK_SIZE = 1;
                    BLOCK_VARIETY = 1;
                    break;

            }

            
            List<Sprite> blockList = new List<Sprite>();
            int totalBlocks = generator.Next(MIN_AMOUNT_BLOCKS, MAX_AMOUNT_BLOCKS);
            int blockType = 0;
            float startX = startPoint;
            float startY = 500;
            String type = "";
            int blockStyle;
            for (int i = 0; i < totalBlocks; i++)
            {
                blockType = generator.Next(SMALLEST_AVAILABLE_BLOCK_SIZE, 4);
                switch (blockType)
                {
                    case (2):
                        type = "mediumBlock";
                        break;
                    case (3):
                        type = "largeBlock";
                        break;
                    default:
                        type = "smallBlock";
                        break;

                }

                blockStyle = generator.Next(BLOCK_VARIETY, 7 );
                switch (blockStyle)
                {
                    case (1):
                        blockList.Add(new MovingBlock(new Vector2(startX, startY), type, new Vector2(50f, 0f), 100, null));
                        break;
                    case (2):
                        blockList.Add(new DisappearingBlock(new Vector2(startX, startY), type, character));
                        break;
                    default:
                        blockList.Add(new Block(new Vector2(startX, startY), type));
                        break;
                }

                //blockList.Add(generator.Next(5) == 0 ? new MovingBlock(new Vector2(startX, startY), type, new Vector2(50f, 0f), 100) : new Block(new Vector2(startX, startY), type));
                //blockList.Add(new Block(new Vector2(startX, startY), type));

                startX += (int)generator.Next((int) MIN_DISTANCE_BETWEEN_BLOCKS, (int) MAX_DISTANCE_BETWEEN_BLOCKS) + blockList[i].Width;
                startY = MathHelper.Clamp(startY+generator.Next((int) -MAX_VERTICAL_DISTANCE, (int) MAX_VERTICAL_DISTANCE), 110, MainGame.Height - 60);
            }
            
            
            return blockList;
        }


        public static List<Sprite> readLevelFromXML(ContentManager content, string name)
        {
            List<Sprite> spriteList = new List<Sprite>();
            try
            {
                BlockSpriteData[] bsd = content.Load<BlockSpriteData[]>(name);
                Block link = null;

                foreach (BlockSpriteData b in bsd)
                {
                    Block m = new Block(b);
                    if (b.type == "Linked Block")
                    {

                        if (link == null)
                            link = m;
                        else
                        {
                            (spriteList[spriteList.Count - 1] as LinkedBlock).partner = m;
                            link = null;
                        }
                    }
                    spriteList.Add(m);
                    Console.WriteLine(b.type + " - ( " + b.xPosition + ", " + b.yPosition);

                }
                if (name.EndsWith("1"))
                    endFlag1 = (Block)spriteList[spriteList.Count - 1];
                if (name.EndsWith("2"))
                    endFlag2 = (Block)spriteList[spriteList.Count - 1];
            }
            catch (Exception e)
            {
                Console.WriteLine("There is no level already created with name: " + name);
                Console.WriteLine(e.Message);
            }
            return spriteList;
        }

        /*public static void writeToXml(ContentManager content, string path, string type, float x, float y)
        {
            cont

        }*/
    }
}